package com.atguigu.gmall.common.config;

import com.alibaba.fastjson.JSON;
import com.atguigu.gmall.common.model.GmallCorrelationData;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.concurrent.TimeUnit;

@Slf4j
@Component
public class MQProducerAckConfig implements RabbitTemplate.ConfirmCallback,
RabbitTemplate.ReturnCallback{

    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Autowired
    private RedisTemplate redisTemplate;

    @PostConstruct  // 1.非静态   2.void   3.执行时期在servlet 初始化
    public void init(){
        rabbitTemplate.setConfirmCallback(this);
        rabbitTemplate.setReturnCallback(this);


    }

    /**
     *  * ConfirmCallback  只确认消息是否正确到达 Exchange 中
     *  * ReturnCallback   消息没有正确到达队列时触发回调，如果正确到达队列不执行
     *  * <p>
     *  * 1. 如果消息没有到exchange,则confirm回调,ack=false
     *  * 2. 如果消息到达exchange,则confirm回调,ack=true
     *  * 3. exchange到queue成功,则不回调return
     *  * 4. exchange到queue失败,则回调return
     * @param correlationData
     * @param b
     * @param s
     */

    /**
     * ConfirmCallback  只确认消息是否正确到达 Exchange 中
     * @param correlationData
     * @param ack
     * @param cause
     * 1. 如果消息没有到exchange,则confirm回调,ack=false
     * 2. 如果消息到达exchange,则confirm回调,ack=true
     */
    @Override
    public void confirm(CorrelationData correlationData, boolean ack, String cause) {



        if (ack) {
            log.info("消息发送成功：" + JSON.toJSONString(correlationData));
        } else {
            log.info("消息发送失败：" + cause + " 数据：" + JSON.toJSONString(correlationData));

            //重试发送消息
            this.retrySendMessage(correlationData);
        }


    }

    /**
     * 发送失败重试方法
     * @param correlationData
     */
    public void retrySendMessage(CorrelationData correlationData) {

        //强转
        GmallCorrelationData gmallCorrelationData= (GmallCorrelationData) correlationData;

        //获取重试次数 0
        int retryCount = gmallCorrelationData.getRetryCount();
        //判断
        if(retryCount>=3){
            //不重发
            log.error("重试次数已到，发送消息失败:"+JSON.toJSONString(gmallCorrelationData));

        }else{
            //加一次
            retryCount+=1;
            System.out.println("重试次数：\t"+retryCount);
            gmallCorrelationData.setRetryCount(retryCount);
            //重试更新redis中的次数
            redisTemplate.opsForValue().set(gmallCorrelationData.getId(),
                    JSON.toJSONString(gmallCorrelationData),10, TimeUnit.MINUTES);

            if(gmallCorrelationData.isDelay()){

                //重试发送消息
                rabbitTemplate.convertAndSend(gmallCorrelationData.getExchange(),
                        gmallCorrelationData.getRoutingKey(),
                        gmallCorrelationData.getMessage(),message -> {

                     //设置超时时间
                            message.getMessageProperties().setDelay(gmallCorrelationData.getDelayTime()*1000);
                    return message;
                        },gmallCorrelationData);


            }else{


                //重试发送消息
                rabbitTemplate.convertAndSend(gmallCorrelationData.getExchange(),
                        gmallCorrelationData.getRoutingKey(),
                        gmallCorrelationData.getMessage(),gmallCorrelationData);


            }


        }




    }

    /**
     * ReturnCallback   消息没有正确到达队列时触发回调，如果正确到达队列不执行
     * @param message
     * @param replyCode
     * @param replyText
     * @param exchange
     * @param routingKey
     */
    @Override
    public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {

        // 反序列化对象输出
        System.out.println("消息主体: " + new String(message.getBody()));
        System.out.println("应答码: " + replyCode);
        System.out.println("描述：" + replyText);
        System.out.println("消息使用的交换器 exchange : " + exchange);
        System.out.println("消息使用的路由键 routing : " + routingKey);


        //获取消息实体id
        String correlationId = (String) message.getMessageProperties().getHeaders().get("spring_returned_message_correlation");
        //根据id从redis中获取消息实体
        String jsonStr = (String) redisTemplate.opsForValue().get(correlationId);
        //转换类型
        GmallCorrelationData correlationData = JSON.parseObject(jsonStr, GmallCorrelationData.class);

        //重试
        this.retrySendMessage(correlationData);


    }

    }

